package net.wangds.persistence.columnoriented.comp;

import net.wangds.data.RemovedEnum;
import net.wangds.log.helper.LogHelper;
import net.wangds.persistence.columnoriented.conv.ToBeanConv;
import net.wangds.persistence.columnoriented.conv.ToColumnsConv;
import net.wangds.persistence.columnoriented.entity.ColumnOrientedEntity;
import net.wangds.persistence.columnoriented.repository.db.ColumnOrientedRepo;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.transaction.Transactional;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * 列式存储操作组件.
 * @param <T> 实体类型.
 * @param <B> 实体类型.
 */
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ColumnOrientedComp<T extends ColumnOrientedEntity, B> {


    /**
     * 操作实体的Repository类实例》
     */
    private final ColumnOrientedRepo<T> repo;
    /**
     * 从列数据列表转换为Bean的转换器.
     */
    private final ToBeanConv<T, B> conv2B;
    /**
     * 从Bean实例转换为列字段记录列表的转换器.
     */
    private final ToColumnsConv<B, T> conv2Cs;

    /**
     *
     * 构造函数.
     * @param repo Repo对象.
     * @param toBeanConv 从列数据列表转换为Bean的转换器.
     * @param toColsConv 从Bean实例转换为列字段记录列表的转换器.
     * @param <R> Repo类型.
     */
    public <R extends ColumnOrientedRepo<T>> ColumnOrientedComp(
            final R repo, ToBeanConv<T, B> toBeanConv, ToColumnsConv<B,T> toColsConv){
        this.repo = repo;
        this.conv2B = toBeanConv;
        this.conv2Cs = toColsConv;
    }

    @Transactional
    public void saveOrUpdate(B bean){
        operateFields(bean,repo::saveAll);
    }

    public void delete(B bean){
        operateFields(bean, (fields)->{
            fields.forEach((cur)-> cur.setRemoved(RemovedEnum.REMOVED));
            repo.saveAll(fields);
        });
    }

    public void erase(B bean){
        operateFields(bean, repo::deleteAll);
    }

    private void operateFields(B bean, Consumer<List<T>> callback){
        this.operateFields(bean, callback, null);
    }

    private void operateFields(B bean, Consumer<List<T>> callback, BiConsumer<List<T>, Throwable> onError){
        if(bean==null||callback==null){
            return;
        }
        Optional.ofNullable(this.conv2Cs.convert(bean)).ifPresent(fields->{
            if(!CollectionUtils.isEmpty(fields)){
                try {
                    callback.accept(fields);
                }catch(Throwable th){
                    if(onError!=null){
                        onError.accept(fields, th);
                    }else{
                        LogHelper.error(th);
                    }
                }
            }
        });
    }











}
